package com.imis.base.util;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.converts.MySqlTypeConvert;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.IColumnType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import com.imis.base.constant.CommonConstant;

import java.util.*;

/**
 * <p>
 * 代码生成器请勿轻易操作！！！
 * </p>
 *
 * @author XinLau
 * @version 1.0
 * @since 2020年2月13日
 */
public class MyBatisPlusCodeGenerator {

    /**
     * 数据库配置信息 DataSourceConfig （需要自定义配置）
     */
    private static final DbType DB_TYPE = DbType.MYSQL;
    private static final String DRIVER_NAME = "com.mysql.cj.jdbc.Driver";
    private static final String HOST = "192.168.1.102";
    private static final String PORT = "3306";
    private static final String DATABASE = "imis_master";
    private static final String USERNAME = "root";
    private static final String PASSWORD = "root";
    private static final String URL = "jdbc:mysql://" + HOST + StringPool.COLON + PORT + StringPool.SLASH + DATABASE + "?characterEncoding=utf8&useSSL=true";

    /**
     * 表名前缀数组 （需要自定义配置）
     */
    private static final String[] TABLE_PREFIX = new String[]{"sys_", "ocr_", "onl_"};

    /**
     * 逻辑删除标识字段名称 （需要自定义配置）
     */
    private static final String LOGIC_DELETE_FIELD_NAME = "";

    /**
     * 自定义模板 （不可修改配置）
     */
    private static final String ENTITY_TEMPLATE_PATH = "/templates/codegeneration/entity.java.ftl";
    private static final String ENTITY_RO_TEMPLATE_PATH = "/templates/codegeneration/entityRo.java.ftl";
    private static final String ENTITY_VO_TEMPLATE_PATH = "/templates/codegeneration/entityVo.java.ftl";
    private static final String CONVERTER_TEMPLATE_PATH = "/templates/codegeneration/entityConverter.java.ftl";
    private static final String CONTROLLER_TEMPLATE_PATH = "/templates/codegeneration/controller.java.ftl";
    private static final String BUS_TEMPLATE_PATH = "/templates/codegeneration/bus.java.ftl";
    private static final String SERVICE_TEMPLATE_PATH = "/templates/codegeneration/service.java.ftl";
    private static final String SERVICE_IMPL_TEMPLATE_PATH = "/templates/codegeneration/serviceImpl.java.ftl";
    private static final String MAPPER_TEMPLATE_PATH = "/templates/codegeneration/mapper.java.ftl";
    private static final String MAPPER_XML_TEMPLATE_PATH = "/templates/codegeneration/mapper.xml.ftl";

    /**
     * 自定义模块名称 （不可修改配置）
     */
    private static final String CLASS_ROUTE = "/src/main/java/com/imis/module/";

    /**
     * 项目路径 （不可修改配置）
     */
    private static final String PROJECT_PATH = CommonConstant.FILE_PATH + StringPool.SLASH;

    /**
     * 包名
     */
    private static String packageName = "";

    /**
     * 模块名
     */
    private static String moduleName = "";

    /**
     * scanner 读取控制台内容
     *
     * @param string - 提示语
     * @return String
     * @author XinLau
     * @since 2019年3月9日下午4:27:43
     */
    @SuppressWarnings("resource")
    private static String scanner(String string) {
        Scanner scanner = new Scanner(System.in);
        System.err.println("请输入一个正确的" + string + "：");
        if (scanner.hasNext()) {
            String name = scanner.next();
            if (StringUtils.isNotBlank(name)) {
                return name;
            }
        }
        throw new MybatisPlusException("请输入正确的" + string + "！！！");
    }

    /**
     * GlobalConfig 相关配置
     *
     * @return GlobalConfig
     * @author XinLau
     * @creed The only constant is change ! ! !
     * @since 2020/2/13
     */
    private static GlobalConfig initGlobalConfig() {
        GlobalConfig globalConfig = new GlobalConfig();
        // 生成存放地址
        globalConfig.setOutputDir(null);
        // 是否覆盖已有文件
        globalConfig.setFileOverride(true);
        // 开启 ActiveRecord 模式
        globalConfig.setActiveRecord(true);
        // 是否在XML中添加二级缓存配置（XML 二级缓存）
        globalConfig.setEnableCache(false);
        // 开启 BaseResultMap（XML ResultMap,生成基本的resultMap）
        // globalConfig.setBaseResultMap(true);
        // 开启 baseColumnList（XML columList,生成基本的SQL片段）
        globalConfig.setBaseColumnList(true);
        // 实体属性 Swagger2 注解
        // globalConfig.setSwagger2(true);
        // 开发人员
        globalConfig.setAuthor(scanner("代码生成作者"));
        // 是否打开输出目录
        globalConfig.setOpen(false);
        // 指定生成的主键的ID类型
        // globalConfig.setIdType(IdType.UUID);
        // 自定义文件命名，注意 %s 会自动填充表实体属性！（各层文件名称方式，例如： %sController 生成 UserController）
        globalConfig.setMapperName("%sMapper");
        globalConfig.setXmlName("%sMapper");
        // 强制使用大写字母I开头
        // globalConfig.setServiceName("%sService");
        globalConfig.setServiceImplName("%sServiceImpl");
        globalConfig.setControllerName("%sController");
        return globalConfig;
    }

    /**
     * DataSourceConfig 数据源配置	【不可修改此方法】
     *
     * @return DataSourceConfig
     * @author XinLau
     * @creed The only constant is change ! ! !
     * @since 2020/2/13
     */
    private static DataSourceConfig initDataSourceConfig() {
        DataSourceConfig dataSourceConfig = new DataSourceConfig();
        // 数据库类型
        dataSourceConfig.setDbType(DB_TYPE);
        dataSourceConfig.setTypeConvert(new MySqlTypeConvert() {
            // 自定义数据库表字段类型转换【可选】
            @Override
            public IColumnType processTypeConvert(GlobalConfig globalConfig, String fieldType) {
                System.out.println("转换类型：" + fieldType);
                // 注意！！processTypeConvert存在默认类型转换，如果不是你要的效果请自定义返回，非如下直接返回
                return super.processTypeConvert(globalConfig, fieldType);
            }
        });
        // 数据库驱动
        dataSourceConfig.setDriverName(DRIVER_NAME);
        // 数据库账号
        dataSourceConfig.setUsername(USERNAME);
        // 数据库密码
        dataSourceConfig.setPassword(PASSWORD);
        // 数据库地址
        dataSourceConfig.setUrl(URL);
        return dataSourceConfig;
    }

    /**
     * StrategyConfig 生成策略配置
     *
     * @return StrategyConfig
     * @author XinLau
     * @creed The only constant is change ! ! !
     * @since 2020/2/13
     */
    private static StrategyConfig initStrategyConfig() {
        StrategyConfig strategyConfig = new StrategyConfig();
        // 全局大写命名 ORACLE 注意
        // strategyConfig.setCapitalMode(true);
        // 此处可以修改为您的表前缀（用来去掉不想要的表前缀）
        strategyConfig.setTablePrefix(TABLE_PREFIX);
        // 数据库表映射到实体的命名策略（表名生成策略）
        strategyConfig.setNaming(NamingStrategy.underline_to_camel);
        // 【实体】是否为Lombok模型（默认 false）
        strategyConfig.setEntityLombokModel(true);
        // 是否生成实体时，生成字段注解
        strategyConfig.setEntityTableFieldAnnotationEnable(true);
        // 设置逻辑删除字段
        // strategyConfig.setLogicDeleteFieldName(LOGIC_DELETE_FIELD_NAME);
        strategyConfig.setInclude(scanner("数据表全称"));
        // 生成 @RestController 控制器
        strategyConfig.setRestControllerStyle(true);
        // strategyConfig.setEntityBuilderModel(true);// 【实体】是否为构建者模型（默认 false）
        // strategyConfig.setEntityColumnConstant(true);// 【实体】是否生成字段常量（默认 false）
        // 自定义实体基类
        strategyConfig.setSuperEntityClass("com.imis.module.base.BaseEntity");
        // 自定义实体基类，公共字段
        strategyConfig.setSuperEntityColumns(new String[]{"id", "create_by", "create_time", "update_by", "update_time"});
        // 自定义 Controller 基类
        strategyConfig.setSuperControllerClass("com.imis.module.base.BaseController");
        // 自定义 Mapper 基类
        strategyConfig.setSuperMapperClass("com.imis.module.base.BaseMapper");
        // 自定义 Service 基类
        strategyConfig.setSuperServiceClass("com.imis.module.base.BaseService");
        // 自定义 ServiceImpl 基类
        strategyConfig.setSuperServiceImplClass("com.imis.module.base.impl.BaseServiceImpl");
        return strategyConfig;
    }

    /**
     * PackageConfig 包相关配置
     *
     * @return PackageConfig
     * @author XinLau
     * @creed The only constant is change ! ! !
     * @since 2020/2/13
     */
    private static PackageConfig initPackageConfig() {
        PackageConfig packageConfig = new PackageConfig();
        // 父包名。如果为空，将下面子包名必须写全部， 否则就只需写子包名
        packageName = scanner("包名");
        packageConfig.setParent("com.imis.module." + packageName);
        // 模块名
        moduleName = scanner("模块名");
        packageConfig.setModuleName(moduleName);
        // Controller包名
        packageConfig.setController("controller");
        // Entity包名
        packageConfig.setEntity("model");
        // Service包名
        packageConfig.setService("service");
        // Service Impl包名
        packageConfig.setServiceImpl("service.impl");
        // Mapper包名
        packageConfig.setMapper("dao");
        // Mapper XML包名
        packageConfig.setXml("dao.xml");
        return packageConfig;
    }

    /**
     * InjectionConfig 自定义模板配置
     *
     * @return InjectionConfig
     * @author XinLau
     * @creed The only constant is change ! ! !
     * @since 2020/2/13
     */
    private static InjectionConfig initInjectionConfig() {
        /**
         * 注入自定义模板配置，可以在 VM 中使用 cfg.abc 设置的值
         * 自定义模板配置，可以 copy 源码 mybatis-plus/src/main/resources/templates 下面内容修改，
         * 放置自己项目的 src/main/resources/templates 目录下, 默认名称一下可以不配置，也可以自定义模板名称
         */
        InjectionConfig injectionConfig = new InjectionConfig() {
            @Override
            public void initMap() {
                Map<String, Object> map = new HashMap<String, Object>(16);
                map.put("Author", "Author : " + this.getConfig().getGlobalConfig().getAuthor());
                this.setMap(map);
            }
        };

        packageName = ConvertUtils.isEmpty(packageName) ? packageName : packageName + StringPool.SLASH;
        moduleName = ConvertUtils.isEmpty(moduleName) ? moduleName : moduleName + StringPool.SLASH;

        // 设置自定义
        List<FileOutConfig> focList = new ArrayList<FileOutConfig>();

        // 自定义 生成转换器 Converter
        focList.add(new FileOutConfig(CONVERTER_TEMPLATE_PATH) {
            @Override
            public String outputFile(TableInfo tableInfo) {
                // 生成路径 + 名称
                return PROJECT_PATH + CLASS_ROUTE + packageName + moduleName + "model/converter/" + tableInfo.getEntityName() + "Converter" + StringPool.DOT_JAVA;
            }
        });
        // 自定义 生成实体类
        focList.add(new FileOutConfig(ENTITY_TEMPLATE_PATH) {
            @Override
            public String outputFile(TableInfo tableInfo) {
                // 生成路径 + 名称
                return PROJECT_PATH + CLASS_ROUTE + packageName + moduleName + "model/po/" + tableInfo.getEntityName() + StringPool.DOT_JAVA;
            }
        });
        // 自定义 生成RO类
        focList.add(new FileOutConfig(ENTITY_RO_TEMPLATE_PATH) {
            @Override
            public String outputFile(TableInfo tableInfo) {
                // 生成路径 +名称  如果你 Entity 设置了前后缀、此处注意的名称会跟着发生变化！！
                return PROJECT_PATH + CLASS_ROUTE + packageName + moduleName + "model/ro/" + tableInfo.getEntityName() + "RO" + StringPool.DOT_JAVA;
            }
        });
        // 自定义 生成VO类
        focList.add(new FileOutConfig(ENTITY_VO_TEMPLATE_PATH) {
            @Override
            public String outputFile(TableInfo tableInfo) {
                // 生成路径 +名称  如果你 Entity 设置了前后缀、此处注意的名称会跟着发生变化！！
                return PROJECT_PATH + CLASS_ROUTE + packageName + moduleName + "model/vo/" + tableInfo.getEntityName() + "VO" + StringPool.DOT_JAVA;
            }
        });
        // 调整Controller生成
        focList.add(new FileOutConfig(CONTROLLER_TEMPLATE_PATH) {
            @Override
            public String outputFile(TableInfo tableInfo) {
                //	Controller生成路径 + 名称		如果Entity设置了前后缀、此处注意Controller的名称会跟着发生变化
                return PROJECT_PATH + CLASS_ROUTE + packageName + moduleName + "controller/" + tableInfo.getEntityName() + ConstVal.CONTROLLER + StringPool.DOT_JAVA;
            }
        });
        // 调整Bus生成
        focList.add(new FileOutConfig(BUS_TEMPLATE_PATH) {
            @Override
            public String outputFile(TableInfo tableInfo) {
                //	Bus生成路径 + 名称		如果Entity设置了前后缀、此处注意Controller的名称会跟着发生变化
                return PROJECT_PATH + CLASS_ROUTE + packageName + moduleName + "bus/" + tableInfo.getEntityName() + "Bus" + StringPool.DOT_JAVA;
            }
        });
        // 调整Service生成
        focList.add(new FileOutConfig(SERVICE_TEMPLATE_PATH) {
            @Override
            public String outputFile(TableInfo tableInfo) {
                //	Service生成路径+名称		如果Entity设置了前后缀、此处注意Service的名称会跟着发生变化
                return PROJECT_PATH + CLASS_ROUTE + packageName + moduleName + "service/I" + tableInfo.getEntityName() + ConstVal.SERVICE + StringPool.DOT_JAVA;
            }
        });
        // 调整ServiceImpl生成
        focList.add(new FileOutConfig(SERVICE_IMPL_TEMPLATE_PATH) {
            @Override
            public String outputFile(TableInfo tableInfo) {
                //	ServiceImpl生成路径+名称		如果Entity设置了前后缀、此处注意ServiceImpl的名称会跟着发生变化
                return PROJECT_PATH + CLASS_ROUTE + packageName + moduleName + "service/impl/" + tableInfo.getEntityName() + ConstVal.SERVICE_IMPL + StringPool.DOT_JAVA;
            }
        });
        // 调整 Mapper 生成目录
        focList.add(new FileOutConfig(MAPPER_TEMPLATE_PATH) {
            @Override
            public String outputFile(TableInfo tableInfo) {
                //	Mapper生成路径+名称		如果Entity设置了前后缀、此处注意Mapper的名称会跟着发生变化
                return PROJECT_PATH + CLASS_ROUTE + packageName + moduleName + "dao/" + tableInfo.getEntityName() + ConstVal.MAPPER + StringPool.DOT_JAVA;
            }
        });
        // 调整 xml 生成目录
        focList.add(new FileOutConfig(MAPPER_XML_TEMPLATE_PATH) {
            @Override
            public String outputFile(TableInfo tableInfo) {
                //	mapper.xml生成路径+名称		如果Entity设置了前后缀、此处注意xml的名称会跟着发生变化
                return PROJECT_PATH + CLASS_ROUTE + packageName + moduleName + "dao/xml/" + tableInfo.getEntityName() + ConstVal.MAPPER + StringPool.DOT_XML;
            }
        });
        // 设置
        injectionConfig.setFileOutConfigList(focList);
        return injectionConfig;
    }

    /**
     * 代码生成器入口
     *
     * @param args
     * @return void
     * @author XinLau
     * @creed The only constant is change ! ! !
     * @since 2020/2/13
     */
    public static void main(String[] args) {
        AutoGenerator mpg = new AutoGenerator();
        // 模板引擎配置 切换成Freemarker
        mpg.setTemplateEngine(new FreemarkerTemplateEngine());
        // GlobalConfig 相关配置
        mpg.setGlobalConfig(initGlobalConfig());
        // DataSourceConfig 数据源配置
        mpg.setDataSource(initDataSourceConfig());
        // StrategyConfig 生成策略配置
        mpg.setStrategy(initStrategyConfig());
        // PackageConfig 包相关配置
        mpg.setPackageInfo(initPackageConfig());
        /**
         * 配置模板（任何一个模块如果设置 空 OR Null 将不生成该模块）
         */
        TemplateConfig templateConfig = new TemplateConfig();
        templateConfig.setEntity(null);
        templateConfig.setEntityKt(null);
        templateConfig.setController(null);
        mpg.setTemplate(templateConfig);
        // InjectionConfig 自定义模板配置
        mpg.setCfg(initInjectionConfig());
        // 生成代码
        mpg.execute();
        // 打印注入设置【可无】
        System.out.println(mpg.getCfg().getMap().get("Author"));
        System.out.println(mpg.getCfg().getMap().toString());
    }

}
